#include "header.h"

extern std::map<uint16_t, int> statusNeighborNodes;
extern LogFile *logFile;
extern LogFile *mainLogFile;

extern bool StatusFileOpen;


char **split(char * string,char *delim){
	char **tokens = NULL;
	char *working = NULL;
	char *token = NULL;
	int index=0;

	tokens  = (char **)malloc(sizeof(char *) * CMD_MAX_TOKENS);
	if(tokens == NULL)
		return NULL;
	working = (char*)malloc(sizeof(char) * strlen(string) + 1);
	if(working == NULL)
		return NULL;

	/* to make sure, copy string to a safe place */
	strcpy(working, string);

	for(index = 0; index < CMD_MAX_TOKENS; index++)
	tokens[index] = NULL;

	token = strtok(working, delim);
	index = 0;

	/* always keep the last entry NULL termindated */
	while((index < (CMD_MAX_TOKENS - 1)) && (token != NULL)) {
		 tokens[index] = (char *)malloc(sizeof(char) * strlen(token) + 1);
		 if(tokens[index] != NULL) {
			 strcpy(tokens[index], token);
			 index++;
			 token = strtok(NULL, delim);
		 }
	}
	free(working);
	return tokens;
}

void HandleStatusNeighbor(char **cmdLineData)
{
	//Command : status neighbor ttl <filename>
	////printf("Inside Handle\n");
	//clear the status node
	statusNeighborNodes.clear();
	
	char statusType = (uint8_t) statusNeighbourMessageType;
	int ttl=0;
	
	//get the ttl
	sprintf(cmdLineData[2],"%d",ttl);
	//printf("TTL = %d\n",ttl);
	
	//get the file name
	string statusfilename = cmdLineData[3];
	remove(statusfilename.c_str());

	//create a header
	Header putHeader = createHeader((uint8_t)statusRequestMessageType,1);
	memcpy(&putHeader.ttl,&ttl,1);
	
	Messages statusMessage;
	
	// 1. set socket descriptor
	statusMessage.socketDescripter=-1;
	
	//2. set header
	statusMessage.Header = (char*)malloc(28*sizeof(char));
	memset(statusMessage.Header,'\0',28);
	memcpy(&statusMessage.Header[0],&putHeader.messageType,1);
	memcpy(&statusMessage.Header[1],putHeader.uoid,20);
	memcpy(&statusMessage.Header[21],&putHeader.ttl,1);
	memcpy(&statusMessage.Header[22],&putHeader.reserved,1);
	memcpy(&statusMessage.Header[23],&putHeader.dataLength,4);

	
	//3. set data with the status type = neighbor
	statusMessage.Data = (char*)malloc(1*sizeof(char));
	memcpy(&statusMessage.Data[0],&statusType,1);

	string uoid = (const char*)putHeader.uoid;
	node.setUOIDStatus(uoid);
	
	//get the entire path
	string path = "./";
	path+=statusfilename;

	//printf("Path = %s\n",path.c_str());
	//open the file  in create mode
	logFile = new LogFile(path,2);
	
	logFile->WriteFilen("V -t * -v 1.0a5");
	//set  the corresponding flags
	StatusFileOpen=true;
	
	logFile->WriteFile("n -t * -s ");
	logFile->WriteFile((int)node.getPort());
	logFile->WriteFilen(" -c red -i black");
	statusNeighborNodes.insert(pair<uint16_t, int>(node.getPort(), (int)node.getPort()));

	
	gettimeofday(&statusFirst, NULL);																		/// get the time when you are SENDING STATUS MSG

	pthread_mutex_lock(&NeighborList);																//Apply LOCK
	multimap<string,int> tempMap = node.getNeighborList();
	pthread_mutex_unlock(&NeighborList);																//RELEASE LOCK

	//printf("MY TOTAL NEIGHBORS = %d\n",tempMap.size());
	multimap<string,int>::iterator it;
		
	// flood the message in the network - write the code that you have written for join
	int tempSock;
	for( it=tempMap.begin(); it!=tempMap.end(); it++)
	{
		//printf("Sending to my neighbors and logging LINKS in file...\n");

		struct timeval t;
		gettimeofday(&t, NULL);
		enterLogMessage(statusMessage,'s',t);


		tempSock = (*it).second;
		//printf("Neighbor socketdesc = %d\n",tempSock);
		pthread_mutex_lock(&WriteQueue[tempSock]);
		writeQueue[tempSock].push_back(statusMessage);			//push to all neighbor the write queue
		pthread_cond_signal(&condition_var_Write[tempSock]);	
		pthread_mutex_unlock(&WriteQueue[tempSock]);

		
		string hostnameport = (*it).first;
		int colon = hostnameport.find(":");
		string portstr = hostnameport.substr(colon+1);
		
		istringstream finalport(portstr);
		uint16_t myport;
		finalport >> myport;
		
		logFile->WriteFile("l -t * -s ");
		logFile->WriteFile((int)node.getPort());
		logFile->WriteFile(" -d ");
		logFile->WriteFile((int)myport);
		logFile->WriteFilen(" -c blue");

	}
}

void HandleShutdownCommand (char **cmdLineData)
{
	// sendNotifyMessage((uint8_t)UserShutdownMessage);
		
	 shutdownFlag=true;
	 
	 //pthread_mutex_lock(&ReadQueue);													//APPLY LOCK
	 pthread_cond_broadcast(&condition_var_Read);
	 //pthread_mutex_unlock(&ReadQueue);													//APPLY LOCK

	sleep(5);
	//printf("Commandline thread EXIT\n");
	pthread_exit(NULL);	
}


void HandleStoreCommand (char **cmdLineData)
{
	// parse the store command
	uint8_t ttl=0;
	//list<string> keywords;
	
	// ttl
	if(cmdLineData[2]==NULL)
	{
		printf("The correct function : store <filename> <ttl> <keywords>\n");
		return;
	}
	
	if((sscanf(cmdLineData[2],"%hhu",&ttl)!=1))
	{
		//flag.showCmdPrompt = true;
		printf("The correct function : store <filename> <ttl> <keywords>\n");
		return;
	}
		
	if(!fileObject.StoreAtPermanentLocation(cmdLineData))
	{
	
		printf("Calling store in command line*******************\n");
		logFile->WriteFilen("Failed to store the file to permanent space!");
		return;
	}
	
	/*std::list<int> forwardStoreNbList;

	node->LockNeighborList();
	for (uint16_t i = 0; i < node->getNeighborList().size(); i++) {
			if(drand48()< node->getStartUpConfig()->getNeighborStoreProb()){
				forwardStoreNbList.push_back(node->getNeighborList().at(i).getSock_fd());
			}
	}
	node->UnlockNeighborList();

	if(forwardStoreNbList.size()!=0)
	{
		ForwardStoreMessage(forwardStoreNbList,index,ttl);
	}

	releaseCmdStringMemory(commandString);*/
	
}

void HandleSearchCommand (char **cmdLineData, int cmdType)
{

	if(strcmp(cmdLineData[1],"filename")==0)
	{
		cout << "filename is : "<<cmdLineData[2]<< endl;
	}
	
	else if(strcmp(cmdLineData[1],"sha1hash")==0)
	{
		cout << "sha1hash is : "<<cmdLineData[2]<< endl;
	}
	
	else
	{
		
		int i=2;
		while(cmdLineData[i]!=NULL)
		{
			string keyword(cmdLineData[i++]);
		
			std::string::size_type k = 0;
			while((k=keyword.find('"',k))!=keyword.npos) 
			{
				keyword.erase(k, 1);
			}
			cout << "keyword : " <<keyword << endl;
		}
		
	}
}


void HandleGetCommand (char **cmdLineData)
{
	cout<< "response number : "<<cmdLineData[1]<<endl;
	cout<< "filename : "<< cmdLineData[2]<<endl;
}

void HandleDeleteCommand (char **cmdLineData)
{
	char *filename=(char *)malloc(FILENAME_MAX *sizeof(char));
	char *formattedsha1=(char *)malloc(41 * sizeof(char));
	char *formattednonce=(char *)malloc(41 * sizeof(char));

	if(sscanf(cmdLineData[2],"%s",filename) != 1 || sscanf(cmdLineData[4],"%s",formattedsha1) != 1 || sscanf(cmdLineData[6],"%s",		formattednonce) != 1)
	{
		printf("Error");
		return;
	}
	
	cout << "filename is :"<< cmdLineData [2]<<endl; 
	cout << "sha1hash is :"<<cmdLineData[4] <<endl; 
	cout << "nonce is :"<< cmdLineData [6]<<endl; 

}


void HandleStatusFile (char **cmdLineData){}

int PerformCommandLineInstruction(char **cmdLineData)
{
int cmdType=-1;

	//status message
	if(strcmp(cmdLineData[0],"status")==0)
	{
		if(strcmp(cmdLineData[1],"neighbors")==0)
		{
			cmdType=STATUSNB;
			//printf("Neighbors:\n");
			HandleStatusNeighbor(cmdLineData);
		}
		else if(strcmp(cmdLineData[1],"files")==0)
		{
			cmdType=STATUSFILE;
			HandleStatusFile(cmdLineData);
		}
		
	}	
	else if(strcmp(cmdLineData[0],"shutdown")==0)
	{
		cmdType=SHUTDOWN;
		HandleShutdownCommand(cmdLineData);
	}
	
	else if(strcmp(cmdLineData[0],"store")==0)
	{
		printf("In store............\n");
		cmdType=STORE;
		HandleStoreCommand(cmdLineData);
	}
	else if(strcmp(cmdLineData[0],"search")==0)
	{
		
		//flag.isSearch=true;
		//flag.prevSearchOrGet=true;
		if(strcmp(cmdLineData[1],"filename")==0)
			cmdType=SEARCHFILE;
		else if(strcmp(cmdLineData[1],"sha1hash")==0)
			cmdType=SEARCHSHA1;
		else if(strcmp(cmdLineData[1],"keywords")==0)
			cmdType=SEARCHKEYWORD;
		else
			return cmdType;
		HandleSearchCommand(cmdLineData,cmdType);
	}
	else if(strcmp(cmdLineData[0],"get")==0)
	{

			cmdType=GET;
			HandleGetCommand(cmdLineData);
	}
	else if(strcmp(cmdLineData[0],"delete")==0)
	{
			//flag.prevSearchOrGet=false;
			cmdType=DELETE;
			HandleDeleteCommand(cmdLineData);
	}
	
	return cmdType;
}
	


void sigintHandler (int signum)
{
     ctrlFlag = true;
}
     
void *commandLineThreadFunction(void *arg)
{	

	//printf("COMMAND LINE THREAD CREATED\n");
	int cmdType = -1;
	char *data= (char *) malloc(512*sizeof(char));
	memset(data,'\0',512*sizeof(char));

	char *delimiters = (char *)"\n= ";
	char **token=NULL;
	char *command = (char *) malloc(512*sizeof(char));
	memset(command,'\0',512*sizeof(char));

	sigset_t set;
	struct sigaction action;
	action.sa_handler = sigintHandler;
	sigaction(SIGINT, &action, NULL);
	pthread_sigmask(SIG_UNBLOCK, &set, NULL);
	


	while (1)
	{
		//sleep(3);
		cout<<"shutdownFlag = "<<shutdownFlag<<endl;
		
			//usleep(100000);
		//printf("servant:%hu> ",node.getPort());
			
		data = fgets(command, 512, stdin); //get command from stdin
		//printf("command =  %s\n",command);
		if(shutdownFlag == true )
		{
			//printf("CommandLine Thread EXIT\n");
			pthread_exit(NULL);
		}
		if(command == NULL)
		{
			continue;
		}
		token = split(command,delimiters);
		for(int i = 0; token[i] != NULL; i++)
		{
			//printf("%02d: %s\n", i, token[i]);
		}
		cmdType=PerformCommandLineInstruction(token);
	}			

	return NULL;
}




